"""
Base grid definitions and abstract interfaces.
"""

using LinearAlgebra
using StaticArrays

"""
    AbstractGrid

Abstract base type for all grid implementations.

All concrete grid types must implement:
- `ndim(grid)`: spatial dimension
- `size(grid)`: grid points per dimension  
- `extent(grid)`: physical extent per dimension
- `spacing(grid)`: grid spacing per dimension
- `coordinate(grid, idx)`: convert grid index to physical coordinate
- `index(grid, coord)`: convert physical coordinate to grid index
"""
abstract type AbstractGrid end

"""
    AbstractCartesianGrid <: AbstractGrid

Abstract base for Cartesian grids with rectangular domains.
"""
abstract type AbstractCartesianGrid <: AbstractGrid end

"""
    AbstractSphericalGrid <: AbstractGrid

Abstract base for grids with spherical symmetry.
"""
abstract type AbstractSphericalGrid <: AbstractGrid end

"""
    AbstractCylindricalGrid <: AbstractGrid

Abstract base for grids with cylindrical symmetry.
"""
abstract type AbstractCylindricalGrid <: AbstractGrid end

# Required interface methods that must be implemented by concrete types

"""
    ndim(grid::AbstractGrid) -> Int

Return the spatial dimension of the grid.
"""
function ndim end

"""
    size(grid::AbstractGrid) -> Tuple{Vararg{Int}}

Return the number of grid points in each dimension.
"""
function Base.size(grid::AbstractGrid) end

"""
    extent(grid::AbstractGrid) -> Tuple{Vararg{Tuple{Float64,Float64}}}

Return the physical extent [min, max] for each dimension.
"""
function extent end

"""
    spacing(grid::AbstractGrid) -> Tuple{Vararg{Float64}}

Return the grid spacing in each dimension.
"""
function spacing end

"""
    volume(grid::AbstractGrid) -> Float64

Return the total volume/area/length of the domain.
"""
function volume(grid::AbstractGrid)
    prod(last(ext) - first(ext) for ext in extent(grid))
end

"""
    cell_volume(grid::AbstractGrid) -> Float64

Return the volume of a single grid cell.
"""
function cell_volume(grid::AbstractGrid)
    prod(spacing(grid))
end

"""
    coordinate(grid::AbstractGrid, idx) -> SVector

Convert grid index to physical coordinate.
"""
function coordinate end

"""
    index(grid::AbstractGrid, coord) -> Tuple{Vararg{Int}}

Convert physical coordinate to grid index.
"""
function index end

"""
    axes(grid::AbstractGrid) -> Tuple{Vararg{AbstractVector}}

Return coordinate axes for each dimension.
"""
function Base.axes(grid::AbstractGrid)
    ntuple(ndim(grid)) do i
        ext = extent(grid)[i]
        n = size(grid)[i]
        range(first(ext), last(ext), length=n)
    end
end

"""
    get_random_point(grid::AbstractGrid) -> SVector

Generate a random point within the grid domain.
"""
function get_random_point(grid::AbstractGrid)
    coords = ntuple(ndim(grid)) do i
        ext = extent(grid)[i]
        first(ext) + (last(ext) - first(ext)) * rand()
    end
    return SVector(coords)
end

"""
    get_axis(grid::AbstractGrid, axis::Int) -> Vector

Get the coordinate values along a specific axis.
"""
function get_axis(grid::AbstractGrid, axis::Int)
    ext = extent(grid)[axis]
    n = size(grid)[axis]
    return collect(range(first(ext), last(ext), length=n))
end

"""
    iter_mirror_points(grid::AbstractGrid, point)

Iterator over mirror points for boundary conditions.
"""
function iter_mirror_points(grid::AbstractGrid, point)
    # Default implementation - can be overridden by specific grid types
    return [point]
end

"""
    compatible_with(grid1::AbstractGrid, grid2::AbstractGrid) -> Bool

Check if two grids are compatible for operations.
"""
function compatible_with(grid1::AbstractGrid, grid2::AbstractGrid)
    return (typeof(grid1) == typeof(grid2) && 
            size(grid1) == size(grid2) && 
            extent(grid1) == extent(grid2))
end

# Default implementations for common operations

"""
    ==(grid1::AbstractGrid, grid2::AbstractGrid) -> Bool

Check if two grids are identical.
"""
function Base.:(==)(grid1::AbstractGrid, grid2::AbstractGrid)
    return compatible_with(grid1, grid2)
end

"""
    hash(grid::AbstractGrid) -> UInt

Compute hash for grid (for use in dictionaries).
"""
function Base.hash(grid::AbstractGrid, h::UInt)
    return hash((typeof(grid), size(grid), extent(grid)), h)
end

"""
    show(io::IO, grid::AbstractGrid)

Display grid information.
"""
function Base.show(io::IO, grid::AbstractGrid)
    print(io, "$(typeof(grid).name.name)(")
    print(io, "size=$(size(grid)), ")
    print(io, "extent=$(extent(grid))")
    print(io, ")")
end